package mx.itesm.twritter.server.map;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import mx.itesm.twritter.server.ontology.Ontology;
import mx.itesm.twritter.server.parser.ProcessedTweet;
import mx.itesm.twritter.shared.map.Node;
import mx.itesm.twritter.shared.map.Way;

import org.dom4j.*;
import org.dom4j.io.SAXReader;

public class OSMParser {
	
	private Map<Integer, Node> nodes; // map with every single node
	private Map<Integer, Way> ways; // map with every single street
	private String file;
	
	public OSMParser(String file) {
		nodes = new HashMap<Integer, Node>();
		ways = new HashMap<Integer, Way>();
		this.file = file;
		init();
	}
	
	public void init() {
		SAXReader reader;
		Document document;
		try {
			reader = new SAXReader();
	        document = reader.read(this.file);
	        Element root = document.getRootElement();
	        
	        // get the nodes
	        for (Iterator<Element> i = root.elementIterator( "node" ); i.hasNext();) {
	            Element xmlNode = (Element) i.next();
	            Integer id = Integer.parseInt(xmlNode.attributeValue("id"));
	            Double lat = Double.parseDouble(xmlNode.attributeValue("lat"));
	            Double lon = Double.parseDouble(xmlNode.attributeValue("lon"));
	            
	            Node node = new Node(id, lat, lon);
	            this.nodes.put(node.getId(),node);
	        }
	        
	        // get the ways
	        for (Iterator<Element> i = root.elementIterator( "way" ); i.hasNext();) {
	            Element xmlNode = (Element) i.next();
	            Integer id = Integer.parseInt(xmlNode.attributeValue("id"));
	            List<Node> wayNodes = new ArrayList<Node>();
	           
	            // get the different nodes
	            List<Element> nodesIds = xmlNode.elements("nd");
	            for(int j=0; j<nodesIds.size(); j++) {
	            	Integer nodeId = Integer.parseInt(nodesIds.get(j).attributeValue("ref"));
	            	Node n = this.nodes.get(nodeId);
	            	wayNodes.add(n);
	            }
	            
	            //get the name
	            String wayName = "";
	            List<Element> wayTags = xmlNode.elements("tag");
	            for(int j=0; j<wayTags.size(); j++) {
	            	String wayTagKey = wayTags.get(j).attributeValue("k");
	            	if(wayTagKey.equals("name")) {
	            		wayName = wayTags.get(j).attributeValue("v");
	            		break;
	            	}
	            }
	            
	            
	            Way way = new Way(id, wayName, wayNodes);
	            
	            //add the ways to the nodes, so we can later know which are crossings
	            if(!wayName.isEmpty()) {
	            	for(Node n : way.getNodes()) {
	            		
	            		if(n != null) 
	            			n.getWays().add(way);
	            	}
	            }
	            
	            this.ways.put(id, way);
	        }
	        
		} catch (DocumentException e) {
			e.printStackTrace();
		}
	}
	
	public List<Node> getCrossings() {
		List<Node> crossings = new ArrayList<Node>();
		
		//look for crossings
        Set<Integer> nodeKeys = this.nodes.keySet();
        for(Iterator<Integer> i = nodeKeys.iterator(); i.hasNext();) {
        	Node node = this.getNodes().get(i.next());
        	if(node.getWays().size() > 1) {
        		crossings.add(node);
        	}
        }
        
        return crossings;
	}
	
	/**
	 * Get the crossings of two sets of ways
	 * @param a
	 * @param b
	 * @return
	 */
	public Set<Node> getCrossings(Set<Way> a, Set<Way> b) {
		Set<Node> nodesA = new TreeSet<Node>();
		Set<Node> nodesB = new TreeSet<Node>();
		
		for (Way w : a) {
			//System.out.println(w.getNodes());
			nodesA.addAll(w.getNodes());
		}
		
		//System.out.println(nodesA.size());
		for (Way w : b) {
			nodesB.addAll(w.getNodes());
		}
		//System.out.println(nodesB.size());
		
		Set<Node> crossings = new TreeSet<Node>();
		
		for(Node na: nodesA) {
			for(Node nb: nodesB) {
				//System.out.println(na.getId());
				//System.out.println(na.getId() + ".equals(" + nb.getId() + ") = " + na.equals(nb));
				if(na.equals(nb)) {
					//System.out.println("qaaa");
					crossings.add(na);
				}
			}
		}
		
		return crossings;
	}
	
	public List<Way> findWaysByName(String name) {
		List<Way> waysCalled = new ArrayList<Way>();	
	
		Set<Integer> keys = this.ways.keySet();
		for(Integer key : keys) {
			Way w = this.ways.get(key);
			//System.out.println(w.getName());
			/*if(w.getName().toLowerCase().contains(name.toLowerCase()) || 
					name.toLowerCase().contains(w.getName().toLowerCase())) {*/
			if(w.getName().equalsIgnoreCase(name)) {
				waysCalled.add(w);
			}
		}
		
		if(waysCalled.isEmpty()) {
			//System.err.println("No ways called \"" + name + "\" found");
		}
		
		return waysCalled;
	}
	
	public ArrayList<Node> findSegment(ProcessedTweet proc) throws Exception {
		
		Set<Way> routeWays = new TreeSet<Way>();
		Set<Way> fromWays = new TreeSet<Way>();
		Set<Way> toWays = new TreeSet<Way>();
		
		ArrayList<Node> nodes = new ArrayList<Node>();
		
		for(String name : proc.getRoute()) {
			//System.out.println(name);
			routeWays.addAll(findWaysByName(name));
		}
		if(routeWays.size() == 0) {
			throw new Exception("No ways called " + proc.getRoute() + " found for ROUTE");
		}
		
		for(String name : proc.getFrom()) {
			fromWays.addAll(findWaysByName(name));
		}
		if(fromWays.size() == 0) {
			throw new Exception("No ways called " + proc.getFrom() + " found for FROM");
		}
		
		for(String name : proc.getTo()) {
			toWays.addAll(findWaysByName(name));
		}
		if(toWays.size() == 0) {
			throw new Exception("No ways called " + proc.getTo() + " found for TO");
		}
		
		Set<Node> crossingsFrom = getCrossings(routeWays, fromWays);
		if(crossingsFrom.size() == 0) {
			throw new Exception("No crossings in ROUTE " + proc.getRoute() + " with FROM " + proc.getFrom() + " found");
		}
		
		Set<Node> crossingsTo = getCrossings(routeWays, toWays);
		if(crossingsTo.size() == 0) {
			throw new Exception("No crossings in ROUTE " + proc.getRoute() + " with TO " + proc.getTo() + " found");
		}
		
		nodes.add((Node) crossingsFrom.toArray()[0]);
		nodes.add((Node) crossingsTo.toArray()[0]);
		
		return nodes;
		
	}
	
	public Map<Integer, Node> getNodes() {
		return nodes;
	}

	public void setNodes(Map<Integer, Node> nodes) {
		this.nodes = nodes;
	}

	public Map<Integer, Way> getWays() {
		return ways;
	}

	public void setWays(Map<Integer, Way> ways) {
		this.ways = ways;
	}

	public String getFile() {
		return file;
	}

	public void setFile(String file) {
		this.file = file;
	}

	public static void main (String args[]) throws Exception {
		OSMParser parser = new OSMParser("../../workspace/Twritter/src/mx/itesm/twritter/server/PyOSMRoute/map.osm");
		
		Ontology o = new Ontology("C:/Users/usuario/workspace/Twritter/src/mx/itesm/twritter/server/ontology/osmRoutes.csv");
		
		String route = "reforma";
		String from = "florencia";
		String to = "bucareli";
		
		ProcessedTweet p = new ProcessedTweet(
				o.searchRoute(route),
				o.searchRoute(from),
				o.searchRoute(to), 
				5);
		
		System.out.println(p);
		
		AStar astar = new AStar("../../workspace/Twritter/src/mx/itesm/twritter/server/PyOSMRoute/", 
				"../../workspace/Twritter/src/mx/itesm/twritter/server/PyOSMRoute/map.osm");
		
		int idStart = parser.findSegment(p).get(0).getId();
		int idEnd =  parser.findSegment(p).get(1).getId();
		
		List<Node> nodes = astar.route(idStart, idEnd);
		
		System.out.println(nodes);
		
		/*
		for(String n: p.from) {
			//System.out.println(parser.findWaysByName(n));
		}
		*/
		//System.out.println(parser.findWaysByName("Circuito Interior"));
		
		/*
		try {
			System.out.println(parser.findSegment(p));
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		
		/*
		Map<Integer, Way> ways = parser.getWays();
		Set<Integer> keys = ways.keySet();
		for(Integer key : keys) {
			if(!ways.get(key).getName().equals(""))
				System.out.println(ways.get(key).getName());
		}
		*/
	}
	
}
